<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2024/3/20
 * Time: 11:46
 */

namespace app\queue\userplanpartimage;

use app\common\lib\Common;
use app\common\lib\Constant;
use app\common\lib\DetectFaceAdvance;
use app\common\lib\RedisQueue;
use app\exception\IndexException;
use app\model\image\ImageModelModel;
use app\model\plan\PromotionPlanPartImageModel;
use app\model\queue\AiImageRequestQueueModel;
use app\model\redis\RedisQueueModel;
use app\model\user\UserNovelChapterPartModel;
use app\model\user\UserPromotionPlanModel;
use app\model\novel\NovelPlanRoleModel;
use support\Cache;
use support\Log;
use Webman\RedisQueue\Consumer;

class AsyncPlanPartAiImage  implements Consumer
{
    // 要消费的队列名
    public $queue = 'asyncPlanPartAiImage';

    // 连接名，对应 plugin/webman/redis-queue/redis.php 里的连接`
    public $connection = 'default';

    protected $resultData = [];

    // 消费
    public function consume($data)
    {
        Common::initQueueConfig();

        $queue = RedisQueueModel::getInstance()->find($data['queueid']);
        $queue->exec_time = date('Y-m-d H:i:s');
        $queue->save();

        $cacheKey = 'async_plan_part_ai_image_'.$data['requestid'];
        try{
            // 无需反序列化
            $result = $this->handle($data,$cacheKey);

            RedisQueue::getInstance()->saveQueue($this->queue,$data,$result);
        }catch (IndexException $e){
            Cache::set($cacheKey,['progress'=>-1,'error'=>$e->getMessage(),'image'=>''],1800);
            RedisQueue::getInstance()->saveQueue($this->queue,$data,['error'=>$e->getMessage()]);
            Common::queueExecptionReport($queue,$data,$e);
        }catch (\Throwable $e){
            Cache::set($cacheKey,['progress'=>-1,'error'=>'生成失败','image'=>''],1800);
            RedisQueue::getInstance()->saveQueue($this->queue,$data,['error'=>$e->getMessage()]);
            Common::queueExecptionReport($queue,$data,$e);
        }
    }

    protected function handle($data,$cacheKey)
    {
        Cache::set($cacheKey,['progress'=>0,'image'=>''],1800);
        //todo 4 生成创作内容片段-ai漫画
        $aiImages = $this->generatedAIImage($data);
        Cache::set($cacheKey,['progress'=>100,'image'=>$aiImages],1800);

        return ['success'];
    }

    protected function generatedAIImage($data)
    {
        $parts = [];
        $width = 1024;
        $height = 1024;
        if(!empty($data['user_planid'])){
            $parts = UserNovelChapterPartModel::getInstance()->getWhere(['user_planid'=>$data['user_planid']])->orderBy('number','asc')->get();
            if(empty($parts->toArray())){
                throw new IndexException(4020,'用户方案片段不存在');
            }
        }elseif (!empty($data['user_partid'])){
            $parts = UserNovelChapterPartModel::getInstance()->getWhere(['id'=>$data['user_partid']])->orderBy('number','asc')->get();
            if(empty($parts->toArray())){
                throw new IndexException(4020,'用户方案片段不存在');
            }
        }

        $userPlanId = $parts[0]['user_planid']??0;
        $userPlan = UserPromotionPlanModel::getInstance()->find($userPlanId);
        //            $modelid = $userPlan['modelid']??'0';
        //            $model = ImageModelModel::getInstance()->find($modelid);
        //            $modelVal = $model ? $model['model'] : 'OIA Illustrator_0.10.safetensors [3328078dcc]';
        if($userPlan==false){
            throw new IndexException(4020,'用户方案不存在');
        }
        $aiImageCommands = json_decode($userPlan['ai_image_command'], true);
        if($aiImageCommands==false){
            throw new IndexException(4020,'生图指令JSON格式错误');
        }
        $modelVal = $aiImageCommands['override_settings']['sd_model_checkpoint']??'';
        if($modelVal==false){
            throw new IndexException(4020,'模型不能为空');
        }

        //如果类型是漫画推文和视频二创，则需要批量生成英文描述词
        if(!empty($data['user_planid']) && in_array($userPlan['plan_type'],[2,3])){
            $contentDatas = [];
            $partsTemp = array_column($parts->toArray(), 'content');
            //长文增强
            $longParts = Common::partsLongText($partsTemp);
            foreach ($parts as $val) {
                if (empty($val['prompt'])) {
                    $longPartsContent = !empty($longParts) ? array_shift($longParts) : '';
                    $content = $longPartsContent ?: $val['content'];
                    $contentDatas[] = ['id' => $val['id'], 'content' => $content];
                }
            }


            if(!empty($contentDatas)){
                $contentTextChunks = array_chunk($contentDatas,50);
                foreach ($contentTextChunks as $contentTexts){
                    $userPlan = UserPromotionPlanModel::getInstance()->find($data['user_planid']);
                    //作品已经加入队列，就不需要在预览这里生成一次
                    if($userPlan && $userPlan['status']>0){
                        break;
                    }

                    $n=1;
                    while (true){
                        $contentData = [];
                        foreach ($contentTexts as $k=>$contents){
                            $contentData[] = (($k+1).'.').preg_replace('/^\d+[\.]+/','',$contents['content']);
                        }
                        $contentText = implode("\n",$contentData);
                        $promptContent = Common::generatedBatchPrompt($contentText);

                        $promptContents = array_values(array_filter(explode("\n",$promptContent)));
                        if(count($promptContents)==count($contentTexts)){
                            foreach ($contentTexts as $k=>$contents){
                                if(isset($promptContents[$k])){
                                    //黑名单词过滤
                                    $aiCommandBlackListWorks = array_filter(explode('|@@|',config('param.ai_command_blacklist_works','')));
                                    $pregs = [];
                                    foreach($aiCommandBlackListWorks as $word){
                                        $pregs[] = ' '.ucfirst(trim($word)).' ';
                                        $pregs[] = ' '.lcfirst(trim($word)).' ';
                                    }
                                    $promptContent = str_replace($pregs,' ',$promptContents[$k]??'');

                                    $promptContent = preg_replace('/^\d+[\.]+/','',$promptContent);

                                    UserNovelChapterPartModel::getInstance()->getWhere(['id'=>$contents['id']])->update(['prompt'=>$promptContent]);
                                }
                            }
                            //满足就跳出
                            break;
                        }
                        if($n>=3){
                            break;
                        }
                        $n++;
                        sleep(1);
                    }
                }

                //重新在读取一次
                $parts = UserNovelChapterPartModel::getInstance()->getWhere(['user_planid'=>$data['user_planid']])->orderBy('number','asc')->get();
            }
        }

        $imageNums = $userPlan['ai_images_nums']??1;
        $aiImageCommand = $userPlan['ai_image_command']??'';

        $requestIdMap = [];

        foreach ($parts as $val){
            if(!empty($data['user_planid'])){
                $userPlan = UserPromotionPlanModel::getInstance()->find($data['user_planid']);
                //作品已经加入队列，就不需要在预览这里生成一次，直接跳出
                if($userPlan && $userPlan['status']>0){
                    break;
                }

                //已经生成过，跳过
                if(!empty($val['ai_images']) || !empty($val['ai_images_arr'])){
                    continue;
                }

                //20240819 拿提前生成好的图，如有就跳过
                if(!empty($val['partid'])){
                    $where = [
                        'partid'=>$val['partid'],
                        'status'=>1,
                    ];
                    $imageData = PromotionPlanPartImageModel::getInstance()->getWhere($where)->limit($imageNums)->get();
                    $images = [];
                    $aiImagFaceArrs = [];
                    foreach ($imageData as $image){
                        $images[] = $image['ai_image'];
                        $aiImagFaceArr = $image['ai_image_face_arr'] ? json_decode($image['ai_image_face_arr'],true)[0] :[];
                        $aiImagFaceArr && $aiImagFaceArrs = array_merge($aiImagFaceArrs,$aiImagFaceArr);
                    }
                    if(count($images)==$imageNums){
                        $update = [];
                        $update['ai_images_bak'] = $images[0];
                        $update['ai_images_arr_bak'] = json_encode($images,256);
                        $update['ai_images'] = $images[0];
                        $update['ai_images_arr'] = json_encode($images,256);
                        count($aiImagFaceArrs)==count($images) && $update['ai_image_face_arr_bak'] = json_encode($aiImagFaceArrs,256);
                        count($aiImagFaceArrs)==count($images) && $update['ai_image_face_arr'] = json_encode($aiImagFaceArrs,256);
                        UserNovelChapterPartModel::getInstance()->getWhere(['id'=>$val['id']])->update($update);

                        foreach ($imageData as $image){
                            $image->userpartid=$val['id'];
                            $image->status=2;
                            $image->user_sn=$val['user_sn'];
                            $image->save();
                        }
                        continue;
                    }
                }
            }

            if(empty($val['prompt'])){
                //todo  获取描述词
                try{
                    $prompt = Common::generatedImagePrompt($val['content'],$val['prompt_ai_command']??'');
                }catch (IndexException $e){
                    if(strpos(strtolower($e->getMessage()),'curl')!==false){
                        $prompt = Common::generatedImagePrompt($val['content'],$val['prompt_ai_command']??'');
                    }else{
                        throw $e;
                    }
                }
                $val->prompt = $prompt;
                $val->save();
                $val['prompt'] = $prompt;
            }

            /*2024-09-18迭代*/
            if (isset($data['is_fix']) && $data['is_fix'] == 1) {//修复图片
                $val['prompt'] = Common::generatedImagePrompt($val['prompt'],'regenerate_image_ai_command');
            }


            //todo 根据描述词生成ai漫画图片
            $prompt = $val['prompt'];
            $source = !empty($data['user_planid']) ? 'user_plan_part_image_view_batch_'.($val['id']??0) : 'user_plan_part_image_view_one_'.($val['id']??0);
            $options = [
                'width' => $width, 'height' => $height, 'batch_size' => $imageNums,
                'seed'  => ($val['ai_image_command_seed'] ?: -1), 'command_template_id' => $userPlan['templateid'] ?? 0
            ];
            if(!empty($val['more_people_prompt'])){
                $options['more_people_prompt'] = $val['more_people_prompt'];
            }
            if(!empty($val['refer_image'])){
                $aiImageCommands['init_images'] = [$val['refer_image']];
                $aiImageCommand = json_encode($aiImageCommands,256);
            }

            $requestId = Common::generatedAIImageByTemplate($aiImageCommand,$prompt,$modelVal,$options,$source,$data['requestid']??'');
            $requestIdMap[$requestId] = $val;
        }

        $aiImages = [];
        foreach ($requestIdMap as $requestId=>$val){
            while (true){
                $aiImages = Common::getAiImageResult($requestId);

                if(!empty($aiImages) && is_array($aiImages)){
                    $update = [];
                    $update['ai_images_bak'] = $aiImages[0];
                    $update['ai_images_arr_bak'] = json_encode($aiImages,256);

                    //获取人脸识别数据
                    $aiImageFaceArrs = [];
                    foreach($aiImages as $k=>$image){
                        $aiImageFaceArr = [];
                        $aiImageFaceArr['link'] = $image;
                        $aiImageFaceArr['faceRectangles'] = [];
                        try{
                            $res = DetectFaceAdvance::getInstance()->getFaceBody($image);
                            if(!empty($res['Data']['FaceRectangles'])){
                                $aiImageFaceArr['faceRectangles'] = $res['Data']['FaceRectangles'];
                            }
                        }catch (\Throwable $e){}

                        $aiImageFaceArrs[] = $aiImageFaceArr;

                        //这里加上休眠是因为QPS 5/s
                        sleep(1);
                    }
                    $update['ai_image_face_arr_bak'] = json_encode($aiImageFaceArrs,256);

                    if(!empty($data['user_planid']) || !empty($data['issave'])){
                        $update['ai_images'] = $aiImages[0];
                        $update['ai_images_arr'] = json_encode($aiImages,256);
                        $update['ai_image_face_arr'] = json_encode($aiImageFaceArrs,256);
                    }

                    UserNovelChapterPartModel::getInstance()->getWhere(['id'=>$val['id']])->update($update);
                    break;
                }
                sleep(3);

                $first = (new AiImageRequestQueueModel())->getWhere(['requestid'=>$requestId])->first();
                if($first && !empty($first['start_time']) && (time()-strtotime($first['start_time']))>300){
                    //如果是批量生成的，跳过，检查下一个
                    if(!empty($data['user_planid'])){
                        break;
                    }else{
                        throw new IndexException(4020,'生成超时');
                    }
                }elseif ($first==false){
                    if(!empty($data['user_planid'])){
                        break;
                    }else{
                        throw new IndexException(4020,'加入队列失败');
                    }
                }
            }
        }

        return $aiImages;
    }

    // 消费失败回调
    /*
    $package = [
        'id' => 1357277951, // 消息ID
        'time' => 1709170510, // 消息时间
        'delay' => 0, // 延迟时间
        'attempts' => 2, // 消费次数
        'queue' => 'send-mail', // 队列名
        'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
        'max_attempts' => 5, // 最大重试次数
        'error' => '错误信息' // 错误信息
    ]
    */
    public function onConsumeFailure(\Throwable $e, $package)
    {
//        echo "consume failure\n";
//        echo $e->getMessage() . "\n";
//        // 无需反序列化
//        var_export($package);

        RedisQueue::getInstance()->saveQueue($this->queue,$package['data'],['error'=>$e->getMessage()]);
    }

}